//  Copyright Fernando Pelliccioni 2011 - 2012. Use, modification and
//  distribution is subject to the Boost Software License, Version
//  1.0. (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//
// For more information, see http://www.boost.org/libs/range/
//

#ifndef BOOST_RANGE_ADAPTOR_BOUNDED_IMPL_HPP
#define BOOST_RANGE_ADAPTOR_BOUNDED_IMPL_HPP

#include <boost/config.hpp>
#ifdef BOOST_MSVC
#pragma warning( push )
#pragma warning( disable : 4355 )
#endif

#include <boost/range/adaptor/argument_fwd.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/iterator/iterator_adaptor.hpp>



namespace boost
{
    namespace adaptors
    {
        struct bounded
        {
            explicit bounded(std::size_t x) : val(x) {}
            std::size_t val;
        };
    }

    namespace range_detail
    {
        template <typename Iter>
        class bounded_iterator
            : public boost::iterator_adaptor< bounded_iterator<Iter>, Iter>
        {
        private:
            typedef boost::iterator_adaptor< bounded_iterator<Iter>, Iter> base;

            typedef BOOST_DEDUCED_TYPENAME base::difference_type bound_type;

            bound_type index_;
			const bound_type top_bound_;

        public:
            explicit bounded_iterator( Iter i, bound_type top_bound )
            : base(i), top_bound_(top_bound), index_(0)
            {
                BOOST_ASSERT( top_bound_ >= 0 && "TopBound Iterator out of bounds" );
            }

         private:
            friend class boost::iterator_core_access;

			template <typename OtherIterator>   
			bool equal( bounded_iterator<OtherIterator> const& x ) const
			{
				return ( index_ >= top_bound_ ) || ( this->base_reference() == x.base_reference() );
			}

			typename super_t::reference dereference() const
			{
				BOOST_ASSERT( index_ < top_bound_ && "TopBound Iterator out of bounds" );
				return *(this->base_reference());
			}

            void increment()
            {
				BOOST_ASSERT( index_ < top_bound_ && "TopBound Iterator out of bounds" );
                ++index_;

				if ( index_ < top_bound_ )
				{
					++(this->base_reference());
				}
            }

            void decrement()
            {
                BOOST_ASSERT( index_ > 0 && "TopBound Iterator out of bounds" );
                --index_;
				--(this->base_reference());
            }

            void advance( bound_type n )
            {
                index_ += n;
                BOOST_ASSERT( index_ >= 0 && index_ <= top_bound_ && "TopBound Iterator out of bounds" );
                this->base_reference() += n;
            }
        };

        template <typename Rng>
        struct bounded_range :
            iterator_range< bounded_iterator<BOOST_DEDUCED_TYPENAME range_iterator<Rng>::type> >
        {
        private:
            typedef bounded_iterator<BOOST_DEDUCED_TYPENAME range_iterator<Rng>::type>
                iter_type;
            typedef iterator_range<iter_type>
                base;
        public:
            template <typename Bound>
            bounded_range( Bound i, Rng& r )
              : base( iter_type(boost::begin(r), i), iter_type(boost::end(r),i) )
            { }
        };

    } // 'range_detail'

    // Make this available to users of this library. It will sometimes be
    // required since it is the return type of operator '|' and
    // bound().
    using range_detail::bounded_range;

    namespace adaptors
    {
        template <typename SinglePassRange>
        inline bounded_range<SinglePassRange>
        operator|( SinglePassRange& r, const bounded& f )
        {
            return bounded_range<SinglePassRange>( f.val, r );
        }

        template <typename SinglePassRange>
        inline bounded_range<const SinglePassRange>
        operator|( const SinglePassRange& r, const bounded& f )
        {
            return bounded_range<const SinglePassRange>( f.val, r );
        }

        template <typename SinglePassRange, typename Bound>
        inline bounded_range<SinglePassRange>
        bound(SinglePassRange& rng, Bound bound_value)
        {
            return bounded_range<SinglePassRange>(bound_value, rng);
        }

        template <typename SinglePassRange, typename Bound>
        inline bounded_range<const SinglePassRange>
        bound(const SinglePassRange& rng, Bound bound_value)
        {
            return bounded_range<const SinglePassRange>(bound_value, rng);
        }
    } // 'adaptors'

}

#ifdef BOOST_MSVC
#pragma warning( pop )
#endif

#endif //BOOST_RANGE_ADAPTOR_BOUNDED_IMPL_HPP
